package com.wzl.thread.controller;

import com.wzl.common.VkPublicResponse;
import com.wzl.thread.methods.TcThreadCallable;
import com.wzl.thread.methods.TcThreadExtend;
import com.wzl.thread.service.TcThreadBasicService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * 多线程基本使用
 * <p>
 * https://blog.csdn.net/weixin_41121133/article/details/109554622?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166298550016800182130183%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=166298550016800182130183&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_positive~default-1-109554622-null-null.142^v47^pc_rank_34_default_3,201^v3^control_2&utm_term=java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E8%AF%A6%E8%A7%A3&spm=1018.2226.3001.4187
 * <p>
 * Java多线程实现方式主要有四种：
 * <p>
 * ① 继承Thread类、实现Runnable接口
 * <p>
 * ② 实现Callable接口通过FutureTask包装器来创建Thread线程
 * <p>
 * ③ 使用ExecutorService、Callable
 * <p>
 * ④ Future实现有返回结果的多线程
 * <p>
 * 其中前两种方式线程执行完后都没有返回值，后两种是带返回值的。
 *
 * @author weizhongli
 * @since 2022-09-12 20:29
 */
@Controller
@Slf4j
public class TcThreadBasicController {

    @Resource
    private TcThreadBasicService tcThreadBasicService;

    /**
     * 继承Thread类创建线程测试
     */
    @RequestMapping("/thread1")
    @ResponseBody
    public VkPublicResponse thread1() {

        TcThreadExtend myThread1 = new TcThreadExtend("子线程1哇哇哇哇");

        //TcThreadExtend myThread1 = new TcThreadExtend();
        //TcThreadExtend myThread2 = new TcThreadExtend();

        /**
         * 修改线程名称
         * 1.调用线程对象的setName()方法。
         * 2.使用线程子类的构造方法赋值。
         */
        //使用setName方法
        //myThread1.setName("子线程1哈哈哈哈");
        myThread1.start();
        //myThread2.start();

        // 启动线程
        //t.run(); // 不会启动线程，不会分配新的分支栈。（这种方式就是单线程。）
        // 这里的代码还是运行在主线程中。
        for (int i = 0; i < 5; i++) {
            log.info("主线程--->" + Thread.currentThread().getId() + " " + "线程名：" + Thread.currentThread().getName() + " " + i);
        }
        return new VkPublicResponse();
    }

    /**
     * 实现Runnable接口创建线程测试
     */
    @RequestMapping("/thread2")
    @ResponseBody
    public VkPublicResponse thread2() {

//        TcThreadRunnable myThread = new TcThreadRunnable();
//        Thread thread = new Thread(myThread);
//        thread.start();
//
//        new Thread(new TcThreadRunnable()).start();
        /**
         * 采用匿名内部类创建线程
         */
        //Runnable runnable = new Runnable() {
        Runnable runnable = () -> {
            for (int i = 0; i < 10; i++) {
                log.info(Thread.currentThread().getName() + "：" + i);
            }
        };

        //创建线程对象
        Thread thread = new Thread(runnable, "我是子线程");
        thread.start();


        return new VkPublicResponse();
    }

    /**
     * 实现Callable接口通过FutureTask包装器来创建Thread线程测试
     */
    @RequestMapping("/thread3")
    @ResponseBody
    public VkPublicResponse thread3() {
        TcThreadCallable vTcThreadCallable = new TcThreadCallable();
        //由Callable<Integer>创建一个FutureTask<Integer>对象：
        FutureTask oneTask = new FutureTask(vTcThreadCallable);
        //注释：FutureTask<Integer>是一个包装器，它通过接受Callable<Integer>来创建，它同时实现了Future和Runnable接口。
        //由FutureTask<Integer>创建一个Thread对象：
        Thread oneThread = new Thread(oneTask);
        oneThread.start();   //至此，一个线程就创建完成了。
        return new VkPublicResponse();
    }

    /**
     * 使用ExecutorService、Callable、Future实现有返回结果的线程
     * <p>
     * 可返回值的任务必须实现Callable接口。类似的，无返回值的任务必须实现Runnable接口。
     * <p>
     * 执行Callable任务后，可以获取一个Future的对象，在该对象上调用get就可以获取到Callable任务返回的Object了。
     * <p>
     * 注意：get方法是阻塞的，即：线程无返回结果，get方法会一直等待。
     * <p>
     * 再结合线程池接口ExecutorService就可以实现传说中有返回结果的多线程了
     */
    @RequestMapping("/thread4")
    @ResponseBody
    public VkPublicResponse thread4() throws ExecutionException, InterruptedException {


        int taskSize = 5;
        // 创建一个线程池
        ExecutorService executor = Executors.newFixedThreadPool(taskSize);
        // 创建多个有返回值的任务
        List<Future> list = new ArrayList<Future>();
        for (int i = 0; i < taskSize; i++) {

            Callable c = new TcThreadCallable(i + " ");
            // 执行任务并获取Future对象
            Future f = executor.submit(c);
            // System.out.println(">>>" + f.get().toString());
            list.add(f);
        }

        // 关闭线程池
        executor.shutdown();

        // 获取所有并发任务的运行结果
        for (Future f : list) {
            // 从Future对象上获取任务的返回值，并输出到控制台
            log.info(">>>" + f.get().toString());
        }

        return new VkPublicResponse();
    }


}